package com.markhsiu.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Reactor implements Runnable {

	private final Selector selector;
	private final ServerSocketChannel serverSocket;
	
	public Reactor(int port) throws IOException{
		//通过调用Selector.open()方法创建一个Selector
		selector = Selector.open();
		serverSocket = ServerSocketChannel.open();
		serverSocket.socket().bind(new InetSocketAddress(port));
		serverSocket.configureBlocking(false);
		//为了将Channel和Selector配合使用，必须将channel注册到selector上。
		SelectionKey sKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
		//与Selector一起使用时，Channel必须处于非阻塞模式下。
		//这意味着FIleChannel与Selector不能一起使用。
		//可以将一个对象或者更多信息附着到SelectionKey上，这样就能方便的识别某个给定的通道
		sKey.attach(new Acceptor());
	}
	
	@Override
	public void run() {
		 try {
			while(!Thread.interrupted()){
				//阻塞到至少有一个通道在你注册的事件上就绪了。
				selector.select();
				Set<SelectionKey> selected = selector.selectedKeys();
				Iterator<SelectionKey> iterator =  selected.iterator();
				while(iterator.hasNext()){
					dispatch(iterator.next());
				}
				//注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。
				//必须在处理完通道时自己移除。
				//下次该通道变成就绪时，Selector会再次将其放入已选择键集中。
				selected.clear();
			}
		} catch (IOException e) {}
	}

	public void dispatch(SelectionKey key){
		Runnable runnable = (Runnable) key.attachment();
		if(runnable != null){
			runnable.run();
		}
	}
	
	class Acceptor implements Runnable{

		@Override
		public void run() {
			 try {
				SocketChannel channel = serverSocket.accept();
				if(channel != null){
					new Handler(selector, channel);
				}
			} catch (Exception e) {}
			
		}
		
	}
	
	final class Handler implements Runnable{

		final SocketChannel socketChannel;
		final SelectionKey key;
		ByteBuffer input = ByteBuffer.allocate(1024);
		ByteBuffer output = ByteBuffer.allocate(1024);
		static final int READING = 0, SENDING = 1;
		int state = READING;
		
		public Handler(Selector sel, SocketChannel channel) throws IOException{
			socketChannel = channel;
			channel.configureBlocking(false);
			// OPtionally try first read now
			key = socketChannel.register(sel, 0);
			key.attach(this);
			key.interestOps(SelectionKey.OP_READ);
			//某个线程调用select()方法后阻塞了，即使没有通道已经就绪，也有办法让其从select()方法返回。
			//只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。阻塞在select()方法上的线程会立马返回。
			//如果有其它线程调用了wakeup()方法，但当前没有线程阻塞在select()方法上，下个调用select()方法的线程会立即“醒来（wake up）”
			sel.wakeup();
		}
		
		boolean inputIsComplete(){return false;}
		boolean outputIsComplete(){return false;}
		void Process(){}
		 
		@Override
		public void run() {
			try {
				if(state == READING){
					read();
				} else if(state == SENDING){
					send();
				}
			} catch (Exception e) {}
		}
		
		void read() throws IOException{
			socketChannel.read(input);
			if(inputIsComplete()){
				Process();
				state = SENDING;
				//Normally also do first write now
				key.interestOps(SelectionKey.OP_WRITE);
			}
		}
		
		void send() throws IOException {
			socketChannel.write(output);
			if(outputIsComplete()){
				key.cancel();
			}
		}
	}
}
